﻿using FastUtil.Base;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace FastUtil.Extra
{
    //引入StackExchange.Redis.dll;Newtonsoft.Json.dll
    public class RedisUtil : BaseUtil
    {
        //系统自定义Key前缀
        public static readonly string SysCustomKey = System.Configuration.ConfigurationManager.AppSettings["RedisKey"] ?? "";
        //127.0.0.1:6379 Redis连接地址
        private static readonly string RedisConnectionString = System.Configuration.ConfigurationManager.AppSettings["RedisConnectionString"];
        private static readonly object Locker = new object();
        private static ConnectionMultiplexer _Instace;
        private static ConnectionMultiplexer GetConnectInstance()
        {
            if (_Instace == null)
            {
                lock (Locker)
                {
                    if (_Instace == null || !_Instace.IsConnected)
                    {
                        _Instace = GetManager();
                    }
                }
            }
            return _Instace;
        }
        private static ConnectionMultiplexer GetManager()
        {
            var connect = ConnectionMultiplexer.Connect(RedisConnectionString);

            //注册如下事件
            connect.ConnectionFailed += MuxerConnectionFailed;
            connect.ConnectionRestored += MuxerConnectionRestored;
            connect.ErrorMessage += MuxerErrorMessage;
            connect.ConfigurationChanged += MuxerConfigurationChanged;
            connect.HashSlotMoved += MuxerHashSlotMoved;
            connect.InternalError += MuxerInternalError;

            return connect;
        }
        private static string GetSysCustomKey(string Key)
        {
            return SysCustomKey + Key;
        }

        #region 事件

        /// <summary>
        /// 配置更改时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void MuxerConfigurationChanged(object sender, EndPointEventArgs e)
        {
            Console.WriteLine("Configuration changed: " + e.EndPoint);
        }

        /// <summary>
        /// 发生错误时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void MuxerErrorMessage(object sender, RedisErrorEventArgs e)
        {
            Console.WriteLine("ErrorMessage: " + e.Message);
        }

        /// <summary>
        /// 重新建立连接之前的错误
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void MuxerConnectionRestored(object sender, ConnectionFailedEventArgs e)
        {
            Console.WriteLine("ConnectionRestored: " + e.EndPoint);
        }

        /// <summary>
        /// 连接失败 ， 如果重新连接成功你将不会收到这个通知
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void MuxerConnectionFailed(object sender, ConnectionFailedEventArgs e)
        {
            Console.WriteLine("重新连接：Endpoint failed: " + e.EndPoint + ", " + e.FailureType + (e.Exception == null ? "" : (", " + e.Exception.Message)));
        }

        /// <summary>
        /// 更改集群
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void MuxerHashSlotMoved(object sender, HashSlotMovedEventArgs e)
        {
            Console.WriteLine("HashSlotMoved:NewEndPoint" + e.NewEndPoint + ", OldEndPoint" + e.OldEndPoint);
        }

        /// <summary>
        /// redis类库错误
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void MuxerInternalError(object sender, InternalErrorEventArgs e)
        {
            Console.WriteLine("InternalError:Message" + e.Exception.Message);
        }

        #endregion 事件

        #region 单数据（字符串）存储
        public static bool Set(string Key, Object Value, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            return DataBase.StringSet(RedistKey, JsonConvert.SerializeObject(Value));
        }
        public static T Get<T>(string Key, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            return JsonConvert.DeserializeObject<T>(DataBase.StringGet(Key));
        }
        #endregion

        #region Hash存储
        public static bool SetHash(string Key, string FieldKey, string FiledValue, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            bool Flag = DataBase.HashSet(RedistKey, FieldKey, FiledValue, When.Always);
            return Flag;
        }
        public static bool SetHash(string Key, string FieldKey, Object FiledValue, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            bool Flag = DataBase.HashSet(RedistKey, FieldKey, JsonConvert.SerializeObject(FiledValue), When.Always);
            return Flag;
        }
        public static string GetHash(string Key, string FieldKey, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            return DataBase.HashGet(Key, FieldKey);
        }
        public static void DelteHash(string Key, string FieldKey,int DataBaseNum = 0) 
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            DataBase.HashDelete(RedistKey,new RedisValue[] { FieldKey });
        }
        public static void BatchDelteHash(string Key, RedisValue[] FieldKeys, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            DataBase.HashDelete(RedistKey,FieldKeys);
        }
        public static Dictionary<string,object> GetAllHash(string Key, int DataBaseNum = 0)
        {
            Dictionary<string,object> dic = new Dictionary<string,object>();
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            HashEntry[] EntryList = DataBase.HashGetAll(Key);
            foreach (HashEntry item in EntryList)
            {
                dic.Add(item.Name, item.Value);
            }
            return dic;
        }
        public static T GetHash<T>(string Key, string FieldKey, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            return JsonConvert.DeserializeObject<T>(DataBase.HashGet(Key, FieldKey));
        }
        public static void SetListHash<T>(string Key, string FieldKeyName, List<T> FieldValueList, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            var batch = DataBase.CreateBatch();
            Type type = typeof(T);
            PropertyInfo KeyProp = type.GetProperty(FieldKeyName);
            foreach (T Item in FieldValueList)
            {
                string FieldKey = Convert.ToString(KeyProp.GetValue(Item, null));
                batch.HashSetAsync(RedistKey, FieldKey, JsonConvert.SerializeObject(Item));
            }
            batch.Execute();
        }
        public static List<T> GetListHash<T>(string Key, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            RedisValue[] Values = DataBase.HashValues(RedistKey);
            List<T> Result = new List<T>();
            foreach (var item in Values)
            {
                T obj = JsonConvert.DeserializeObject<T>(item);
                Result.Add(obj);
            }
            return Result;
        }
        public static bool ExistHash(string Key, string FieldKey, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            return DataBase.HashExists(Key, FieldKey);
        }
        #endregion

        #region List操作
        /// <summary>
        /// 移除指定ListId的内部List的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void ListRemove<T>(string Key, T Value, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            DataBase.ListRemove(RedistKey, JsonConvert.SerializeObject(Value));
        }

        /// <summary>
        /// 获取指定key的List
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static List<T> ListRange<T>(string Key, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            List<T> result = new List<T>();
            foreach (var item in DataBase.ListRange(RedistKey))
            {
                result.Add(JsonConvert.DeserializeObject<T>(item));
            }
            return result;
        }

        /// <summary>
        /// 入队
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void ListRightPush<T>(string Key, T Value, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            DataBase.ListRightPush(RedistKey, JsonConvert.SerializeObject(Value));
        }

        /// <summary>
        /// 出队
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T ListRightPop<T>(string Key, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            return JsonConvert.DeserializeObject<T>(DataBase.ListRightPop(RedistKey));
        }

        /// <summary>
        /// 入栈
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void ListLeftPush<T>(string Key, T Value, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            DataBase.ListLeftPush(RedistKey, JsonConvert.SerializeObject(Value));
        }

        /// <summary>
        /// 出栈
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T ListLeftPop<T>(string Key, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            return JsonConvert.DeserializeObject<T>(DataBase.ListLeftPop(RedistKey));
        }

        /// <summary>
        /// 获取集合中的数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static long ListLength(string Key, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            return DataBase.ListLength(Key);
        }
        #endregion

        #region 通用操作
        public static bool ExistKey(string Key, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            return DataBase.KeyExists(Key);
        }
        public static bool SetKeyExpire(string Key, TimeSpan Expire, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            return DataBase.KeyExpire(Key, Expire);
        }

        public static bool DelteKey(string Key, int DataBaseNum = 0)
        {
            string RedistKey = GetSysCustomKey(Key);
            var DataBase = GetConnectInstance().GetDatabase(DataBaseNum);
            return DataBase.KeyDelete(Key);
        }
        #endregion

        #region 发布订阅

        /// <summary>
        /// Redis发布订阅  订阅
        /// </summary>
        /// <param name="subChannel"></param>
        /// <param name="handler"></param>
        public static void Subscribe(string ChannelName, Action<RedisChannel, RedisValue> handler = null)
        {
            ISubscriber sub = GetConnectInstance().GetSubscriber();
            sub.Subscribe(ChannelName, (channel, message) =>
            {
                //接收订阅消息，处理逻辑
                if (handler == null)
                {
                    Console.WriteLine(ChannelName + " 订阅收到消息：" + message);
                }
                else
                {
                    handler(channel, message);
                }
            });
        }

        /// <summary>
        /// Redis发布订阅  发布
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="channel"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static long Publish<T>(string ChannelName, T msg)
        {
            ISubscriber sub = GetConnectInstance().GetSubscriber();
            return sub.Publish(ChannelName, JsonConvert.SerializeObject(msg));
        }

        /// <summary>
        /// Redis发布订阅  取消订阅
        /// </summary>
        /// <param name="channel"></param>
        public static void Unsubscribe(string ChannelName)
        {
            ISubscriber sub = GetConnectInstance().GetSubscriber();
            sub.Unsubscribe(ChannelName);
        }

        /// <summary>
        /// Redis发布订阅  取消全部订阅
        /// </summary>
        public static void UnsubscribeAll()
        {
            ISubscriber sub = GetConnectInstance().GetSubscriber();
            sub.UnsubscribeAll();
        }

        #endregion 发布订阅
       
    }
}
